/** Copyright (C) 2007 Pascal Richter
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package jlfm.lastfm.client.impl.simple;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import jlfm.lastfm.client.LastFmClient;
import jlfm.lastfm.client.LastFmClientFaultException;
import jlfm.lastfm.client.LastFmLoginException;
import jlfm.lastfm.client.NotLoggedInException;
import jlfm.lastfm.client.OperationNotSupportedException;
import jlfm.lastfm.client.TrackMetadata;
import jlfm.lastfm.client.command.LastFmCommandResult;
import jlfm.lastfm.client.events.LastFmClientListener;
import jlfm.lastfm.client.events.TrackChangeListener;
import jlfm.lastfm.client.impl.LastFmHttpCommandClient;
import jlfm.lastfm.client.impl.advanced.Credential;
import jlfm.lastfm.client.impl.advanced.SimpleCredential;
import jlfm.lastfm.station.LastFmStation;
import jlfm.lastfm.station.LastFmUrlStation;
import jlfm.lastfm.tools.LastFmClientTools;
import jlfm.lastfm.tools.url.LastFmCommandURL;

public class SimpleLastFmClient implements LastFmClient {

	private String sessionID = null;

	private String version = "1.3.1.1";

	LastFmHttpCommandClient lastFmHttpCommandClient = new LastFmHttpCommandClient();

	PlayListManager plManager;

	private Credential credential = null;

	protected List<TrackChangeListener> trackChangeListeners = new ArrayList<TrackChangeListener>();

	protected List<LastFmClientListener> clientListeners = new ArrayList<LastFmClientListener>();

	protected ListenerInformer informer = new ListenerInformer();

	public SimpleLastFmClient() {
		plManager = new PlayListManager(this);
	}

	public void setProxyConfiguration(ProxyConfiguration proxyConfiguration) {
		lastFmHttpCommandClient.setProxyConfiguration(proxyConfiguration);
	}

	public void banTrack() {
		// TODO: implement
		throw new OperationNotSupportedException();
	}

	protected void checkSession() throws NotLoggedInException {
		if (!isLoggedIn() && credential != null) {
			try {
				login(credential.getUserName(), credential.getPassword());
			} catch (LastFmLoginException e) {
				throw new LastFmClientFaultException(e);
			}
		}

		if (!isLoggedIn()) {
			throw new NotLoggedInException();
		}

	}

	private void setCredential(Credential credential) {
		this.credential = credential;
	}

	public void login(String username, String password)
			throws LastFmLoginException {
		login(new SimpleCredential(username, password));
	}

	public void login(Credential c) throws LastFmLoginException {
		setCredential(c);
		String password = encryptPasswordToMD5(c.getPassword());

		LastFmCommandURL url = new LastFmCommandURL();
		url.setFile("/radio/handshake.php");
		url.addQueryParameter("version", version);
		url.addQueryParameter("platform", "unknown");
		url.addQueryParameter("username", c.getUserName());
		url.addQueryParameter("passwordmd5", password);

		LastFmCommandResult loginProps;

		loginProps = lastFmHttpCommandClient.execute(url.getURL());

		if (loginProps.get("session").equalsIgnoreCase("FAILED")) {
			throw new LastFmLoginException("Login Failed: "
					+ loginProps.get("msg"));
		}
		setSessionID(loginProps.get("session"));
		informer.loggedIn(c.getUserName(), getSessionID());
	}

	public boolean isLoggedIn() {
		return sessionID != null && sessionID.length() > 0;
	}

	public TrackMetadata getTrackInfo() {
		checkSession();
		return plManager.getActualTrack();
	}

	private String encryptPasswordToMD5(String password)
			throws LastFmClientFaultException {
		try {
			return LastFmClientTools.getMd5Hash(password);
		} catch (NoSuchAlgorithmException e) {
			throw new LastFmClientFaultException(
					"MD5 Encryption not supported. You have to give "
							+ "your password already encrypted!");
		}
	}

	public void logoff() {
		// TODO implement
		throw new OperationNotSupportedException();
	}

	public void loveTrack() {
		// TODO implement
		throw new OperationNotSupportedException();
	}

	public void skipTrack() {
		checkSession();
		LastFmCommandURL urll = new LastFmCommandURL();
		urll.setFile("/radio/control.php");
		urll.addQueryParameter("session", getSessionID());
		urll.addQueryParameter("command", "skip");
		urll.addQueryParameter("debug", "0");

		LastFmCommandResult reply = lastFmHttpCommandClient.execute(urll
				.getURL());
		if (!reply.get("response").equalsIgnoreCase("OK")) {
			throw new LastFmClientFaultException("Skip track failed!");
		}
	}

	public void tuneIn(LastFmStation station) {
		tuneIn(station.getUrl());
	}

	public void tuneIn(String lastfmUrl) throws LastFmClientFaultException {
		checkSession();
		LastFmCommandURL url = new LastFmCommandURL();
		url.setFile("/radio/adjust.php");
		url.addQueryParameter("session", getSessionID());
		url.addQueryParameter("url", lastfmUrl);
		url.addQueryParameter("debug", "0");

		LastFmCommandResult tuneInProps = lastFmHttpCommandClient.execute(url
				.getURL());
		if (!tuneInProps.get("response").equalsIgnoreCase("OK")) {
			throw new LastFmClientFaultException("Tune in failed!");
		}
		informer.tunedIn(new LastFmUrlStation(lastfmUrl));
	}

	protected String getSessionID() {
		return sessionID;
	}

	private void setSessionID(String sessionID) {
		this.sessionID = sessionID;
	}

	public InputStream getStream() throws LastFmClientFaultException {
		checkSession();
		TrackMetadata m = plManager.getNextTrack();
		try {
			return lastFmHttpCommandClient.getInputStream(new URL(m
					.getStreamUrl()));
		} catch (MalformedURLException e) {
			throw new LastFmClientFaultException(e);
		}
	}

	public void addTrackChangeListener(TrackChangeListener listener) {
		trackChangeListeners.add(listener);
	}

	public void removeTrackChangeListener(TrackChangeListener listener) {
		trackChangeListeners.remove(listener);
	}

	public void addClientListener(LastFmClientListener listener) {
		clientListeners.add(listener);
	}

	public void removeClientListener(LastFmClientListener listener) {
		clientListeners.remove(listener);
	}

	public class ListenerInformer implements TrackChangeListener,
			LastFmClientListener {

		public void trackChanged(TrackMetadata metadata) {
			for (TrackChangeListener l : trackChangeListeners) {
				l.trackChanged(metadata);
			}
		}

		public void loggedIn(String user, String session) {
			for (LastFmClientListener l : clientListeners) {
				l.loggedIn(user, session);
			}
		}

		public void tunedIn(LastFmStation station) {
			for (LastFmClientListener l : clientListeners) {
				l.tunedIn(station);
			}
		}

	}
}
